package com.atguigu.com.seckill.sched;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.com.seckill.feign.CouponFeign;
import com.atguigu.com.seckill.feign.ProductFeign;
import com.atguigu.com.seckill.to.SeckillSkuRedisTo;
import com.atguigu.com.seckill.vo.SeckillSession;
import com.atguigu.com.seckill.vo.SeckillSkuRelationEntity;
import com.atguigu.com.seckill.vo.SkuInfoVo;
import com.atguigu.common.utils.R;
import org.redisson.api.RLock;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class seckillSched {

    @Autowired
    CouponFeign couponFeign;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    ProductFeign productFeign;

    @Autowired
    RedissonClient redissonClient;

    public final String SECKILL_SKU = "seckill";
    private final String SECKILL_STOCK = "seckill:stock";
    private final String SECKILL_UPLOAD_LOCK = "seckill:upload:lock";
    private final String SESSIONS_CACHE_PREFIX = "seckill:session";

    /**
     * 定时上架
     * 把秒杀商品存入到redis中
     * 使用分布式锁 避免重复上架
     */
    @Scheduled(cron = "0 0 3 * * ?")
    public void Sched(){
        RLock lock = redissonClient.getLock(SECKILL_UPLOAD_LOCK);
        lock.lock(10, TimeUnit.SECONDS);//设置时间
        try {
            uploadSeckill();
        }finally {
            if (lock.isLocked()){
                if (lock.isHeldByCurrentThread()){
                    lock.unlock();//解锁
                }
            }
        }
    }

    /**
     * 商品上架
     */
    private void uploadSeckill() {
        R seckillTime = couponFeign.getSeckillTime();
        if (seckillTime.getcode()==0){
            //获取当前时间秒杀商品信息
            List<SeckillSession> seckillSessions = seckillTime.getData(new TypeReference<List<SeckillSession>>() {
            });
            if (seckillSessions!=null&&seckillSessions.size()>0){
                //首先把每一个秒杀时间段放入redis中 缓存活动信息
                saveSessionInfos(seckillSessions);
                seckillSessions.stream().forEach(item->{
                    saveSeckillSku(item);
                });
            }
        }
    }

    /**
     * 缓存活动信息
     * 存入秒杀开始时间到结束时间的商品id
     * @param data
     */
    private void saveSessionInfos(List<SeckillSession> data) {
        data.stream().forEach(item->{
            Date startTime = item.getStartTime();
            Date endTime = item.getEndTime();
            String key = SESSIONS_CACHE_PREFIX+startTime.toString()+"_"+endTime.toString();//活动开始到结束时间段

            List<SeckillSkuRelationEntity> entities = item.getEntities();
            Boolean aBoolean = redisTemplate.hasKey(key); //判断这个时间段商品是否已经上架
            if (aBoolean){
                //根据每个时间段的id值获取这个时间段的所有商品id
                List<Long> collect = entities.stream().map(id -> id.getSkuId()).collect(Collectors.toList());
                redisTemplate.opsForList().leftPush(key,collect.toString());
            }
        });
    }
    /**
     * 保存秒杀商品的详细信息
     * 散列 存入商品id 和 商品详情
     */
    private void saveSeckillSku(SeckillSession session){
        BoundHashOperations<String, Object, Object> boundHashOps = redisTemplate.boundHashOps(SECKILL_SKU);
        session.getEntities().stream().forEach((item) ->{
            //判断redis中是否存中该商品
            if (!boundHashOps.hasKey(item.getSkuId().toString())){
                SeckillSkuRedisTo seckillSkuRedisTo = new SeckillSkuRedisTo();
                //1.商品秒杀信息
                BeanUtils.copyProperties(item,seckillSkuRedisTo);
                Integer seckillLimit = item.getSeckillLimit().intValue();
                seckillSkuRedisTo.setSeckillLimit(seckillLimit);

                //2.商品基本信息
                R info = productFeign.info(item.getSkuId());
                SkuInfoVo skuInfoVo = info.getData(new TypeReference<SkuInfoVo>() {
                });
                seckillSkuRedisTo.setSkuInfoVo(skuInfoVo);

                //3.商品的上架时间和结束时间
                seckillSkuRedisTo.setStartTime(session.getStartTime().getTime());
                seckillSkuRedisTo.setEndTime(session.getEndTime().getTime());

                //4.随机码
                String replace = UUID.randomUUID().toString().replace("_","");
                seckillSkuRedisTo.setRandomCods(replace);

                //5.信号量 为商品数量
                RSemaphore semaphore = redissonClient.getSemaphore(SECKILL_STOCK + replace);
                BigDecimal seckillCount = item.getSeckillCount();

                int i = seckillCount.intValue();
                semaphore.trySetPermits(i);


                //放入redis中
                String string = JSON.toJSONString(seckillSkuRedisTo);
                boundHashOps.put(item.getSkuId().toString(),string);
            }
        });
    }
}
